/* 
* Copyright (C) 2011-2018 ShenZhen iBOXSaaS Information Technology Co.,Ltd. 
* 
* All right reserved. 
* 
* This software is the confidential and proprietary 
* information of iBOXSaaS Company of China. 
* ("Confidential Information"). You shall not disclose 
* such Confidential Information and shall use it only 
* in accordance with the terms of the contract agreement 
* you entered into with iBOXSaaS inc. 
* 
*/

package org.iboxpay.open.common.token;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import org.iboxpay.open.common.constants.ResultEnums;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;

/**
 * RSA生成token工具类
 * The class RSAUtil.
 * Description: Token签名算法(RS256)
 *
 * @author: liuhebin
 * @since: 2018年2月8日
 *
 */
public class RsaUtil {

    /**
     * 发行者
     */
    public static final String ISS = "iboxpay.com";

    /**
     * token类型
     */
    public static final String IBP_TOKEN_TYPE = "ibp-token-type";

    /**
     * 生成token, 默认有效期5分钟
     * @param params    自定义key、value
     * @param privateKey 私钥
     * @return
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     */
    public static String getToken(Map<String, Object> params, String privateKeyStr) throws InvalidKeySpecException, NoSuchAlgorithmException {
        return getToken(params, privateKeyStr, 5 * 60);
    }

    /**
     * 生成token
     * @param params    自定义key、value
     * @param privateKey 私钥
     * @param exp       有效时间, 单位：秒
     * @return
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     */
    public static String getToken(Map<String, Object> params, String privateKeyStr, int exp) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Calendar c = Calendar.getInstance();
        Date now = new Date();
        c.add(Calendar.SECOND, exp);
        return Jwts.builder().setClaims(params).setIssuer(ISS).setIssuedAt(now) 
                .setNotBefore(now) 
                .setExpiration(c.getTime()) 
                .signWith(SignatureAlgorithm.RS256, BaseKeyUtil.getPrivateKey(privateKeyStr)).compact();
    }

    /**
     * 生成token
     * @param params    自定义key、value
     * @param privateKey 私钥
     * @param tokenType token类型, "passport" or "mcht" or "hhr" or "gooda"
     * @param exp       有效时间, 单位：秒
     * @return
     * @throws NoSuchAlgorithmException 
     * @throws InvalidKeySpecException 
     */
    public static String getToken(Map<String, Object> claims, Map<String, Object> header, String privateKeyStr, int exp) throws InvalidKeySpecException, NoSuchAlgorithmException {
        // 非空验证
        if (header.get("typ") == null || "".equals(header.get("typ").toString().trim())) {
            throw new RuntimeException("tokenType不能为空");
        }
        if (header.get("alg") == null) {
            throw new RuntimeException("alg标识签名算法 不能为空");
        }
        Calendar c = Calendar.getInstance();
        Date now = new Date();
        c.add(Calendar.SECOND, exp);
        return Jwts.builder().setHeaderParams(header).setClaims(claims).setIssuer(ISS)
                .setIssuedAt(now) 
                .setNotBefore(now) 
                .setExpiration(c.getTime()) 
                .signWith((SignatureAlgorithm) header.get("alg"), BaseKeyUtil.getPrivateKey(privateKeyStr))
                .compact();
    }

    
    /**
	 * 
	 * @param token 
	 * @param publicKeyStr 公钥base64编码后字符串
	 * @return
	 * @throws Exception 
	 */
	public static VerifyResult<Claims> verify(String token, String publicKeyStr) {
		VerifyResult<Claims> result = new VerifyResult<Claims>();
		try {
			Jws<Claims> jws = Jwts.parser().setSigningKey(BaseKeyUtil.getPublicKey(publicKeyStr)).parseClaimsJws(token);
			Claims claims = jws.getBody();
			result.setResultCode(ResultEnums.success);
			result.setResult(claims);
		} catch (SignatureException | MalformedJwtException e) {
			// jwt 解析错误
			// don't trust the JWT!
			result.setResultCode(ResultEnums.tokenInvalid);
			result.setErrorDesc(ResultEnums.tokenInvalid.getDesc());
		} catch (ExpiredJwtException e) {
			// jwt 已经过期，在设置jwt的时候如果设置了过期时间，这里会自动判断jwt是否已经过期，
			// 如果过期则会抛出这个异常，我们可以抓住这个异常并作相关处理。
			result.setResultCode(ResultEnums.tokenExpired);
			result.setErrorDesc(ResultEnums.tokenExpired.getDesc());
		} catch (Exception e) {
			result.setResultCode(ResultEnums.tokenSystemError);
			result.setErrorDesc(ResultEnums.tokenSystemError.getDesc());
		}
		return result;
	}

}
